/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.playground.client.websockets.application;

import com.playground.client.websockets.entity.message.Action;
import com.playground.client.websockets.entity.message.request.RequestMessage;
import com.playground.client.websockets.entity.message.request.RoomRegistrationRequestMessage;
import com.playground.client.websockets.entity.message.request.WriteToChatRequestMessage;
import com.playground.client.websockets.entity.message.response.WriteToChatResponseMessage;
import com.playground.client.websockets.socket.PlayerSocket;
import com.playground.client.websockets.util.JsonUtil;
import com.playground.core.entity.Game;
import com.playground.core.entity.LoggedPlayer;
import com.playground.core.entity.PlayerStatus;
import com.playground.core.entity.Room;
import com.playground.core.repository.RoomRepository;
import com.playground.core.service.LoginService;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.jboss.logging.Logger;

/**
 *
 * @author Tiago Peres
 */
public abstract class RoomApplication extends PlaygroundApplication{
    private static HashMap<Long, PlayerSocket> playerIndex = new HashMap<Long, PlayerSocket>();
    
    protected RoomRepository roomRepository;
    
    public RoomApplication(LoginService loginService, RoomRepository roomRepository){
        super(loginService);
        this.roomRepository = roomRepository;
    }
    
    @Override
    protected boolean registerPlayerToSocket(PlayerSocket socket, long playerId, String key) throws CloneNotSupportedException{
        if (super.registerPlayerToSocket(socket, playerId, key)){
            playerIndex.put(playerId, socket);
            synchronized(this){
                socket.getPlayer().setStatus(PlayerStatus.playing);
                Room r = socket.getPlayer().getRoom();
                if (null == r.getGame()){
                    Game game = makeGame(r.getPlayers());
                    r.setGame(game);
                }
                Iterator<LoggedPlayer> players = r.getPlayers().iterator();
                boolean allRegistered = true;
                while (players.hasNext() && allRegistered){
                    LoggedPlayer p = players.next();
                    PlayerSocket ps = getPlayerSocketByPlayerId(p.getId());
                    if (null == ps || !PlayerStatus.playing.equals(ps.getPlayer().getStatus())){
                        Logger.getLogger(RoomApplication.class.getName()).log(Logger.Level.INFO, socket.getPlayer().getId() + ": " + p.getId() + " has a null.");
                        allRegistered = false;
                    }
                }
                if (allRegistered){
                    Logger.getLogger(RoomApplication.class.getName()).log(Logger.Level.INFO, socket.getPlayer().getId() + ": all players are registered.");
                    informGameStart(r);
                }
                return true;
            }
        } else {
            return false;
        }
    }
    
    @Override
    protected boolean parseMessage(PlayerSocket socket, RequestMessage message){
        //if a message was sent to the chat window call writeToChat(socket, message)
        if (Action.writeToChat.equals(message.getAction())){
            writeToChat(socket, ((WriteToChatRequestMessage) message).getText());
        }
        // TODO: else if the message says "quit the game" calls giveUp(socket)
        return true;
    }
    
    /**
     * Registers a room to the given socket. It's used when a player is entering
     * a room to play a game.
     * 
     * @param socket the playerSocket requesting the register process
     * @param roomId the id of the room to be entered
     */
    /*private synchronized void registerRoomToSocket(PlayerSocket socket, long roomId){
        Logger.getLogger(RoomApplication.class.getName()).log(Logger.Level.INFO, socket.getPlayer().getId() + " requested room registration");
        Room r = roomRepository.findOne(roomId);
        if (null != r && r.getPlayers().contains(socket.getPlayer())){
            socket.getPlayer().setRoom(r);
            if (null == r.getGame()){
                Game game = makeGame();
                // set gameTitle?
                game.setPlayers(r.getPlayers());
                game.setUpGame();
                r.setGame(game);
            }
            Iterator<LoggedPlayer> players = r.getPlayers().iterator();
            boolean allRegistered = true;
            while (players.hasNext() && allRegistered){
                LoggedPlayer p = players.next();
                PlayerSocket ps = getPlayerSocketByPlayerId(p.getId());
                if (null == ps || null == ps.getPlayer().getRoom()){
                    Logger.getLogger(RoomApplication.class.getName()).log(Logger.Level.INFO, socket.getPlayer().getId() + ": " + p.getId() + " has a null.");
                    allRegistered = false;
                }
            }
            if (allRegistered){
                Logger.getLogger(RoomApplication.class.getName()).log(Logger.Level.INFO, socket.getPlayer().getId() + ": all players have a room registered.");
                informGameStart(r);
            }
        }
        
    }*/
    
    /**
     * Quit a user from a game
     * 
     * @param socket the player giving up
     */
    protected abstract void giveUp(PlayerSocket socket);
    
    protected void writeToChat(PlayerSocket socket, String message){
        WriteToChatResponseMessage response = new WriteToChatResponseMessage();
        response.setSenderId(socket.getPlayer().getId());
        response.setSuccess(true);
        response.setText(message);
        sendToAll(socket.getPlayer().getRoom(), JsonUtil.getGson().toJson(response, WriteToChatResponseMessage.class));
    }
    
    public static PlayerSocket getPlayerSocketByPlayerId(long id) {
        return playerIndex.get(id);
    }
    
    // send a message to all players in a room
    protected void sendToAll(Room room, String message){
        List<LoggedPlayer> players = room.getPlayers();
        for (LoggedPlayer p : players){
            getPlayerSocketByPlayerId(p.getId()).send(message);
        }
    }
    
    protected abstract void informGameStart(Room room);
    
    // Fabric method
    protected abstract Game makeGame(List<LoggedPlayer> players);
}
